InVesalius Plugins

Thiago Franco de Moraes
Paulo Henrique Junqueira Amorim

09/03/2022

InVesalius Plugins

Mínimo necessário

Localização

  • invesalius3/plugins
  • $HOME/.config/invesalius/plugins

Uma pasta para cada plugin

Pasta de plugins

Arquivos necessário

my_plugin/
├── __init__.py
├── main.py
└── plugin.json

__init__.py

  • Cada plugin é um módulo Python
  • Utilizar Python para interagir com InVesalius
    • Mas pode utilizar outras linguagens adicionalmente
  • Utilizar WXPython para interface gráfica

plugins.json

{  
    "name": "Nome do plugin",  
    "description": "Uma pequena descrição",  
    "enable-startup": false 
}

enable-startup indica se o plugin pode ser utilizado sem uma imagem carregada.

main.py

Responsável por iniciar o plugin

import wx
from pubsub import pub as Publisher

import invesalius.data.slice_ as slc
from invesalius import project


def load():
    print("Iniciando o plugin")

Bibliotecas disponíveis

❯ cat requirements.txt 
Cython==0.29.24
Pillow==9.0.0
Pypubsub==4.0.3
configparser==5.0.1
h5py==2.10.0
imageio==2.9.0
nibabel==3.2.1
numpy==1.21.2
plaidml-keras==0.7.0
psutil==5.8.0
pyserial==3.5
python-gdcm==3.0.9.1
scikit-image==0.18.3
scipy==1.7.1
vtk==9.0.3
wxPython==4.1.1
Theano==1.0.5
torch==1.9.1
pyacvd==0.2.7

Acesso aos dados do InVesalius

project

  • Acesso às máscara, superfícies, medidas.
  • Arquivo invesalius/project.py
from invesalius import project
prj = project.Project()
prj.mask_dict
prj.surface_dict
prj.measurement_dict

slice

  • Arquivo invesalius/data/slice_.py
from invesalius.data.slice_ import Slice

slc = Slice()
image = slc.matrix # matriz do numpy
first_pixel = image[0, 0, 0]
current_mask = slc.current_mask

mask

  • Arquivo invesalius/data/mask.py
from invesalius.data.slice_ import Slice

slc = Slice()
mask = slc.create_new_mask() # matriz do numpy
first_pixel = mask[0, 0, 0]

Eventos PubSub

# test.py
from pubsub import pub as Publisher
def a(value):
    print(f"função a recebeu {value}")

def b(value):
    print(f"função b recebeu {value}")

Publisher.subscribe(a, "test_pubsub")
Publisher.subscribe(b, "test_pubsub")
Publisher.sendMessage("test_pubsub", value=42)
$ python test.py
função a recebeu 42
função b recebeu 42

Alguns eventos e mensagens do InVesalius

"Update slice viewer"
"Update slice viewer {orientation}"
"Reload actual slice"
"Reload actual slice {orientation}"
("Set scroll position", orientation)
"Render volume viewer"
"Load surface actor into viewer"
"Remove surface actor from viewer"
"Load volume into viewer"
"Unload volume"
"Close project data"
"Enable style"
"Disable actual style"

Styles

  • Utilizado para adicionar e trabalhar com eventos nos slices e no 3D.
# my_style.py
from invesalius.data import styles

# `Base3DInteractorStyle` ou `DefaultInteractorStyle`
class MyStyle(styles.BaseImageInteractorStyle):
    def __init__(self, viewer):
        super().__init__(viewer)
        self.AddObserver("LeftButtonPressEvent", self.OnMouseLeftPress)
    def OnMouseLeftPress(self, obj, evt):
        print(obj, evt)

Styles

# main.py
import wx
from invesalius.data import styles
from pubsub import pub as Publisher
from . import my_style

def load():
    # Adicionar o `Style` ao `StyleManager` do InVesalius:
    style_id = styles.Styles.add_style(my_style.MyStyle, 2)
    # Habilitar o `style` criado:
    Publisher.sendMessage("Enable style", style=style_id)

Ao alterar uma máscara

# A primeira posição em cada orientação indica se o threshold já foi aplicado.
# Se for alterar marcar essas posições com 1
mask.matrix[0] = 1
mask.matrix[:, 0, :] = 1
mask.matrix[:, :, 0] = 1
mask.matrix[1:, 1:, 1:] = 42
# Indicar que a imagem foi editada
mask.was_edited = True
# Usar o modified para a renderização do preview da máscara ser recarregado
mask.modified()
# Limpar o histório (Ctrl-Z Ctrl-Y)
mask.clear_history()
# Limpara os caches
self.viewer.discard_mask_cache(all_orientations=True, vtk_cache=True)
# Recarregar máscara atual
Publisher.sendMessage('Reload actual slice')

Plugins de exemplo

https://github.com/tfmoraes/inv3_plugins_examples